﻿#include <hacks/base/ev_timer.h>

namespace hacks {

    typedef ev_buddy<ev_timer> ev_timer_buddy;

    ev_timer::ev_timer(const shared_ev_loop &loop,
                       ev_timer_functor_t &&tick_f,
                       std::size_t timeout,
                       std::size_t repeat) :
            ev_timer(loop,
                     std::forward<ev_timer_functor_t>(tick_f),
                     nullptr,
                     timeout, repeat) {
    }

    ev_timer::ev_timer(const shared_ev_loop &loop,
                       ev_timer_functor_t &&tick_f,
                       ev_timer_functor_t &&close_f,
                       std::size_t timeout,
                       std::size_t repeat)
            : event_loop_(loop),
              timer_(nullptr),
              timer_tick_functor_(std::forward<ev_timer_functor_t>(tick_f)),
              timer_close_functor_(std::forward<ev_timer_functor_t>(close_f)),
              timeout_(timeout),
              repeat_(repeat),
              commit_once_flag_(),
              timer_status_(evt_init),
              user_data_(nullptr) {
        assert(event_loop_);
        assert(timer_tick_functor_);
    }

    std::size_t ev_timer::get_repeat() const {
        return repeat_;
    }

    std::size_t ev_timer::get_timeout() const {
        return timeout_;
    }

    void ev_timer::commit() {
        std::call_once(commit_once_flag_, [this]() {
            auto ins = shared_from_this();
            assert(ins);
            ins->event_loop_->run_in_loop([ins]() {
                ins->init();
                ev_timer_buddy *buddy = new ev_timer_buddy;
                buddy->shd_ins = ins;
                ins->timer_->data = buddy;
                uv_timer_start(ins->timer_, &ev_timer::uv_timer_event_callback, ins->timeout_, ins->repeat_);
            });
            ins->timer_status_.store(evt_commited, std::memory_order_release);
        });
    }

    ev_timer::~ev_timer() {
        assert(timer_status_ == evt_stopped || timer_status_ == evt_commited);
        delete timer_;
    }

    void ev_timer::restart_timer() const {
        assert(timer_status_ == evt_commited);
        event_loop_->run_in_loop([ins = shared_from_this()]() {
            uv_timer_start(ins->timer_, &ev_timer::uv_timer_event_callback, ins->timeout_, ins->repeat_);
        });
    }

    void ev_timer::stop_timer() {
        ev_evt_state exp = evt_commited;
        if (!timer_status_.compare_exchange_strong(exp, evt_stopping, std::memory_order_acq_rel)) {
            return;
        }
        event_loop_->run_in_loop([ins = shared_from_this()]() {
            uv_timer_stop(ins->timer_);
            uv_close(reinterpret_cast<uv_handle_t *>(ins->timer_), &ev_timer::uv_timer_close_callback);
        });
    }

    shared_ev_loop &ev_timer::get_event_loop() {
        return event_loop_;
    }

    void ev_timer::set_userdata(void *udata) {
        user_data_ = udata;
    }

    void *ev_timer::get_userdata() const {
        return user_data_;
    }

    void ev_timer::init() {
        const auto raw_loop = event_loop_->get_raw_loop();
        timer_ = new uv_timer_t;
        uv_timer_init(raw_loop, timer_);
    }

    void ev_timer::uv_timer_event_callback(uv_timer_t *timer) {
        auto *buddy = static_cast<ev_timer_buddy *>(timer->data);
        assert(buddy);
        buddy->shd_ins->ev_timer_tick_callback();
    }

    void ev_timer::uv_timer_close_callback(uv_handle_t *hd) {
        auto *buddy = static_cast<ev_timer_buddy *>(hd->data);
        assert(buddy);
        buddy->shd_ins->timer_status_.store(evt_stopped, std::memory_order_release);
        buddy->shd_ins->ev_timer_close_callback();
        delete buddy;
    }

    void ev_timer::ev_timer_close_callback() {
        //callback to user
        if (timer_close_functor_) {
            timer_close_functor_(*this);
        }
    }

    void ev_timer::ev_timer_tick_callback() {
        assert(timer_tick_functor_);
        timer_tick_functor_(*this);
    }
}
